import React, { Component } from 'react'
import { findDOMNode } from 'react-dom';
import _ from 'underscore'
import $ from 'jquery'
import cx from 'classnames'
let sizerStyle = { position: 'absolute', visibility: 'hidden', height: 0, width: 0, overflow: 'scroll', whiteSpace: 'nowrap' };

export class SelectOption extends Component {
  render() {
    let { optionClass, op: { className } } = this.props;
    if (className) {
      optionClass += ` ${ className }`;
    }

    if (this.props.op.disabled) {
      return (
        <div className={ optionClass }>
          { this.props.op.label }
        </div>
      );
    } else {
      return (
        <div
          className={ optionClass }
          onMouseEnter={ this.props.mouseEnter }
          onMouseLeave={ this.props.mouseLeave }
          onMouseDown={ this.props.mouseDown }
          onClick={ this.props.mouseDown } >
          { this.props.op.label }
        </div>
      );
    }
  }
}

SelectOption.defaultProps = {
  getSelectedItem: null, // 获取当前项：如果没选中则返回 undefined
  groupContainer: null, // 所属的 SelectOptionGroup 组件实例对象
  selectContainer: null, // Select 组件实例对象
  op: {}, // option 原数据
  optionClass: "", // 当前选项的 class 数据
  mouseEnter: null, // 鼠标进入事件
  mouseLeave: null, // 鼠标移出事件
  mouseDown: null // 鼠标按下事件
}

export class SelectOptionGroupTitle extends Component {
  render() {
    if (!this.props.group) {
      return null;
    } else if (this.props.group === '--') {
      return (<div className="group-title line-spliter"></div>);
    } else {
      return (
        <div className="group-title">
          { this.props.group }
        </div>
      );
    }
  } // render
}

SelectOptionGroupTitle.defaultProps = {
  group: null // null 表示没有分组；-- 表示横线；其它表示正常渲染
}

export class SelectOptionGroup extends Component {
  renderOptionParameters(op) {
    let isFocused = this.props.focusedValue === op.value;

    let optionClass = cx({
      'Select-option': true,
      'is-focused': isFocused,
      'is-disabled': op.disabled
    });

    return {
      getSelectedItem: () => _.find(this.props.selectedValues(), item => item.value === op.value),
      groupContainer: () => this.refs.group,
      selectContainer: () => this.props.selectContainer(), // Select 组件实例对象
      op,
      optionClass,
      mouseEnter: e => this.props.focusOption(op),
      mouseLeave: e => this.props.unfocusOption(op),
      mouseDown: e => this.props.selectValue(op, e)
    };
  }
  renderOptions(options) {
    return Object.keys(options).map(function(key) {
      let op = options[key];

      return (
        <this.props.SelectOption
          key={ 'option-' + _.isObject(op.value) ? JSON.stringify(op.value) : op.value }
          {...this.renderOptionParameters(op)} />
      );
    }, this);
  } // renderOptions

  /**
   * 获取是否为分组数据：true 表示为分组数据，否则返回 false 。
   */
  isGroupOptions(item) {
    return item.group !== undefined && item.options;
  } // isGroupOptions

  render() {
    let list = [];
    // 全部都存在分组数据标识，只要不是 undefined 就认为是存在的。
    if (_.every(this.props.filteredOptions, p => this.isGroupOptions(p))) {
      list = _.chain(this.props.filteredOptions)
              .map((value, index) => {
                let options = this.renderOptions(value.options);
                if (options.length) {
                  return (
                    <div key={ index }>
                      {
                        <this.props.SelectOptionGroupTitle group={ value.group } />
                      }
                      { options }
                    </div>
                  );
                } else {
                  return null;
                }
              })
              .filter(item => item !== null)
              .value();
    } else {
      list = this.renderOptions(this.props.filteredOptions);
    }

    return list.length
      ? (
        <div ref="group">{ list }</div>
      ) : (
        <div ref="group" className="Select-noresults">
        {
          this.props.asyncOptions && !this.props.inputValue
            ? this.props.searchPromptText
            : this.props.noResultsText
        }
        </div>
      );
  } // render
}

SelectOptionGroup.defaultProps = {
  selectedValues: () => [],
  SelectOption: SelectOption,
  SelectOptionGroupTitle: SelectOptionGroupTitle,
  filteredOptions: {},
  focusedValue: null,
  focusOption: null,
  unfocusOption: null,
  selectValue: null,
  SelectOption: null,
  asyncOptions: null,
  inputValue: null,
  searchPromptText: null,
  noResultsText: null,
}

class Input extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      inputWidth: props.minWidth
    }
  }
  componentDidMount() {
    this.copyInputStyles();
    this.updateInputWidth();
  }
  componentDidUpdate() {
    this.updateInputWidth();
  }
  copyInputStyles() {
    if (!window.getComputedStyle) {
      return;
    }
    let inputStyle = window.getComputedStyle(this.refs.input);
    let widthNode = this.refs.sizer;
    widthNode.style.fontSize = inputStyle.fontSize;
    widthNode.style.fontFamily = inputStyle.fontFamily;
    if (this.props.placeholder) {
      let placeholderNode = this.refs.placeholderSizer;
      placeholderNode.style.fontSize = inputStyle.fontSize;
      placeholderNode.style.fontFamily = inputStyle.fontFamily;
    }
  }
  updateInputWidth() {
    let $sizer = this.refs.sizer
    if (typeof $sizer.scrollWidth === 'undefined') {
      return;
    }
    let newInputWidth;
    if (this.props.placeholder) {
      newInputWidth = Math.max($sizer.scrollWidth, this.refs.placeholderSizer.scrollWidth) + 2;
    } else {
      newInputWidth = $sizer.scrollWidth + 2;
    }
    if (newInputWidth < this.props.minWidth) {
      newInputWidth = this.props.minWidth;
    }
    if (newInputWidth !== this.state.inputWidth) {
      this.setState({
        inputWidth: newInputWidth
      });
    }
  }
  focus() {
    this.refs.input.focus();
  }
  select() {
    this.refs.input.select();
  }
  render() {
    let nbspValue = (this.props.value || '').replace(/ /g, '&nbsp;');
    let wrapperStyle = this.props.style || {};
    wrapperStyle.display = 'inline-block';
    let inputStyle = this.props.inputStyle || {};
    inputStyle.width = this.state.inputWidth;
    let placeholder = this.props.placeholder ? <div ref="placeholderSizer" style={sizerStyle}>{this.props.placeholder}</div> : null;
    return (
      <div className={this.props.className} style={wrapperStyle}>
        <input {...this.props} ref="input" className={this.props.inputClassName} style={inputStyle} />
        <div ref="sizer" style={sizerStyle} dangerouslySetInnerHTML={{ __html: nbspValue }} />
        {placeholder}
      </div>
    );
  }
}
Input.propTypes = {
  value: React.PropTypes.any,                 // field value
  defaultValue: React.PropTypes.any,          // default field value
  onChange: React.PropTypes.func,             // onChange handler: function(newValue) {}
  style: React.PropTypes.object,              // css styles for the outer element
  className: React.PropTypes.string,          // className for the outer element
  minWidth: React.PropTypes.oneOfType([       // minimum width for input element
    React.PropTypes.number,
    React.PropTypes.string
  ]),
  inputStyle: React.PropTypes.object,         // css styles for the input element
  inputClassName: React.PropTypes.string      // className for the input element
}
Input.defaultProps = {
  minWidth: 1
}
class Option extends React.Component{
  blockEvent(ev) {
    ev.stopPropagation();
  }
  render(){
    return (
      <div className="Select-item">
        <span className="Select-item-icon"
          onMouseDown={this.blockEvent}
          onClick={this.props.onRemove}
          onTouchEnd={this.props.onRemove}>&times;</span>
        <span className="Select-item-label">{this.props.displayText || this.props.label}</span>
      </div>
    );
  }
}
Option.propTypes = {
  label: React.PropTypes.string.isRequired
}
export default class Select extends React.Component{
  constructor(props, context){
    super(props, context);

    let options = props.options;
    if (_.every(props.options, p => this.isGroupOptions(p))) {
      options = _.chain(props.options).pluck('options').flatten(true).value();
    }

    this.state = {
      options,
      isFocused: false,
      isOpen: props.isOpen || false,
      isLoading: false
    }
    this._closeMenu = this._closeMenu.bind(this)
    this.handleMouseDown = this.handleMouseDown.bind(this)
    this.handleInputBlur = this.handleInputBlur.bind(this)
    this.handleInputFocus = this.handleInputFocus.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
    this.clearValue = this.clearValue.bind(this)
  }

  /**
   * 获取是否为分组数据：true 表示为分组数据，否则返回 false 。
   */
  isGroupOptions(item) {
    return item.group !== undefined && item.options;
  } // isGroupOptions

  _closeMenu(e){
    let el = e.target
      , container = this.refs.wrapper
    if(this.state.isOpen&&!$.contains(container,el)){
      this.setState({
        isOpen:false
      })
    }
  }
  componentWillMount(){
    this._optionsCache = {};
    this._optionsFilterString = '';
    this.setState(this.getStateFromValue(this.props.value));
  }
  componentDidMount(){
    $('body').on('click',this._closeMenu)
  }
  componentWillReceiveProps(newProps){
    if (JSON.stringify(newProps.options) !== JSON.stringify(this.props.options)) {
      this.setState({
        options: newProps.options,
        filteredOptions: this.filterOptions(newProps.options)
      });
    }
    if (newProps.value !== this.state.value) {
      this.setState(this.getStateFromValue(newProps.value, newProps.options));
    }

    if (newProps.isOpen !== this.props.isOpen) {
      this.setState({
        isOpen: newProps.isOpen
      }, this._setScrollTop);
    }
  }
  componentDidUpdate(){
    let self = this;
    if (!this.props.disabled && this._focusAfterUpdate) {
      clearTimeout(this._blurTimeout);

      this._focusTimeout = setTimeout(function() {
        let input = self.getInputNode();
        if (input) {
          input.focus();
        }
        self._focusAfterUpdate = false;
      }, 50);
    }

    if (this._focusedOptionReveal) {
      this._setScrollTop();

      this._focusedOptionReveal = false;
    }
  }
  componentWillUnmount() {
    clearTimeout(this._blurTimeout);
    clearTimeout(this._focusTimeout);
    $('body').off('click',this._closeMenu)
  }
  focus() {
    this.getInputNode().focus();
  }
  getStateFromValue(value, options) {
    if (!options) {
      options = this.state.options;
    }
    // reset internal filter string
    this._optionsFilterString = '';

    let values = this.initValuesArray(value, options),
      filteredOptions = this.filterOptions(options, values);

    if (this.props.openDisabled) {
      _.chain(filteredOptions)
        .filter(p => _.some(values, r => r.value === p.value))
        .each(p => p.disabled = true);
    }

    if (this.props.addNoMatch) {
      let attachedValues = _.filter(this.state && this.state.attachedValues || [], p => !_.some(values, r => r.value === p.value));
      values = values.concat(attachedValues);
    }
    return {
      value: values.map(function(v) { return v.value; }).join(this.props.delimiter),
      values: values,
      inputValue: '',
      filteredOptions: filteredOptions,
      placeholder: !this.props.multi && values.length ? (values[0].displayText || values[0].label) : this.props.placeholder,
      focusedOption: !this.props.multi && values.length ? values[0] : filteredOptions[0]
    };
  }
  initValuesArray(values, options) {
    if (!_.isArray(values)) {
      if (typeof values === 'string') {
        values = values.split(this.props.delimiter);
      } else {
        values = values ? [values] : [];
      }
    }
    return values.map(function(val) {
      if (typeof val === 'string') {
        for (let key in options) {
          if (options.hasOwnProperty(key) && options[key] && options[key].value === val) {
            return options[key];
          }
        }
        return { value: val, label: val };
      } else {
        return val;
      }
    });
  }
  setValue(value, focusAfterUpdate) {
    if (focusAfterUpdate || focusAfterUpdate === undefined) {
      this._focusAfterUpdate = true;
    }
    let newState = this.getStateFromValue(value);
    newState.isOpen = false;
    this.fireChangeEvent(newState);
    this.setState(newState);
  }
  selectValue(value, event) {
    if (!this.props.multi) {
      if (this.props.showSingleTooltip && window.TooltipForData) {
        window.TooltipForData.hideTooltip();
      }

      if (!value && event && event.keyCode === 13 && this.props.addNoMatch) {
        value = {
          label: this.state.inputValue,
          value: this.state.inputValue
        };

        this.setState(({ attachedValues }) => {
          attachedValues = attachedValues || [];
          if (!_.some(attachedValues, p => p.value === value.value)) {
            attachedValues = [value];
          }

          return {
            values: [],
            attachedValues
          };
        }, () => this.setValue(value));
      } else {
        this.setState({
          values: _.map(this.state.values, p => {
            p.disabled = false;
            return p
          })
        });

        this.setValue(value);
      }
    } else if (value) {
      // TODO: set multi disabled status

      this.addValue(value);
    }
    // this._unbindCloseMenuIfClickedOutside();
  }
  addValue(value) {
    this.setValue(this.state.values.concat(value));
  }
  popValue() {
    if (this.props.addNoMatch) {
      let item = _.last(this.state.values, 1)[0];
      if (!item) {
        return;
      }

      this.removeValue(item);
    } else {
      this.setValue(this.state.values.slice(0, this.state.values.length - 1));
    }
  }
  removeValue(valueToRemove) {
    if (this.props.addNoMatch) {
      this.state.attachedValues = _.filter((this.state.attachedValues || []), v => v.value !== valueToRemove.value);
    }
    this.setValue(this.state.values.filter(function(value) {
      return value !== valueToRemove;
    }));
  }
  clearValue(event) {
    // if the event was triggered by a mousedown and not the primary
    // button, ignore it.
    if (event && event.type === 'mousedown' && event.button !== 0) {
      return;
    }
    this.setValue(null);
  }
  resetValue() {
    this.setValue(this.state.value === '' ? null : this.state.value);
  }
  getInputNode () {
    let input = this.refs.input;
    return this.props.searchable ? input : input;
  }
  fireChangeEvent(newState) {
    if ((newState.values[0] !== this.state.values[0] ||
         newState.values.length !== this.state.values.length) &&
         this.props.onChange) {
      this.props.onChange(this.props.multi?newState.values:newState.values[0]);
    }
  }
  handleMouseDown(event) {
    // if the event was triggered by a mousedown and not the primary
    // button, or if the component is disabled, ignore it.
    if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) {
      return;
    }
    event.stopPropagation();
    event.preventDefault();
    if (this.state.isFocused) {
      this.setState({
        isOpen: true
      }, this._setScrollTop);
    } else {
      this._openAfterFocus = true;
      this.getInputNode().focus();
    }
  }
  handleInputFocus(event) {
    let newIsOpen = this.state.isOpen || this._openAfterFocus;
    this.setState({
      isFocused: true,
      isOpen: newIsOpen
    }, this._setScrollTop);
    this._openAfterFocus = false;

    if (this.props.onFocus) {
      this.props.onFocus(event);
    }
  }
  handleInputBlur(event) {
    let self = this;
    this._blurTimeout = setTimeout(function() {
      if (self._focusAfterUpdate) return;

      self.setState({
        isFocused: false
      });
    }, 50);

    if (this.props.onBlur) {
      this.props.onBlur(event);
    }
  }
  handleKeyDown(event) {
    if (this.state.disabled) return;
    switch (event.keyCode) {
      case 8: // backspace
        if (!this.state.inputValue) {
          this.popValue();
        }
      return;
      case 9: // tab
        if (event.shiftKey || !this.state.isOpen || !this.state.focusedOption) {
          return;
        }
        this.selectFocusedOption(event);
      break;
      case 13: // enter
        this.selectFocusedOption(event);
      break;
      case 27: // escape
        if (this.state.isOpen) {
          this.resetValue();
        } else {
          this.clearValue();
        }
      break;
      case 38: // up
        this.focusPreviousOption();
      break;
      case 40: // down
        this.focusNextOption();
      break;
      default: return;
    }
    event.preventDefault();
  }
  // Ensures that the currently focused option is available in filteredOptions.
  // If not, returns the first available option.
  _getNewFocusedOption(filteredOptions) {
    for (let key in filteredOptions) {
      if (filteredOptions.hasOwnProperty(key) && filteredOptions[key] === this.state.focusedOption) {
        return filteredOptions[key];
      }
    }
    return filteredOptions[0];
  }
  _setScrollTop() {
    if (this.refs.menu) {
      var focusedDOM = $(this.refs.menu).find('.is-focused')[0];
      if (!focusedDOM) {
        return;
      }

      var menuDOM = findDOMNode(this.refs.menu);
      var focusedRect = focusedDOM.getBoundingClientRect();
      var menuRect = menuDOM.getBoundingClientRect();

      if (focusedRect.bottom > menuRect.bottom || focusedRect.top < menuRect.top) {
        menuDOM.scrollTop = (focusedDOM.offsetTop + focusedDOM.clientHeight - menuDOM.offsetHeight);
      }
    }
  }
  handleInputChange(event) {
    // assign an internal letiable because we need to use
    // the latest value before setState() has completed.
    this._optionsFilterString = event.target.value;

    if (this.props.asyncOptions) {
      this.setState({
        isLoading: true,
        inputValue: event.target.value
      });
    } else {
      let filteredOptions = this.filterOptions(this.state.options);
      this.setState({
        isOpen: true,
        inputValue: event.target.value,
        filteredOptions: filteredOptions,
        focusedOption: this._getNewFocusedOption(filteredOptions)
      }, this._setScrollTop);
    }
  }
  filterOptions(options, values) {
    if (!this.props.searchable) {
      return options;
    }
    let filterValue = this._optionsFilterString;
    let exclude = (values || this.state.values).map(function(i) {
      return i.value;
    });
    if (this.props.filterOptions) {
      return this.props.filterOptions.call(this, options, filterValue, exclude);
    } else {
      if (!options) {
        return [];
      }

      let data = _.chain(options);
      if (_.every(options, item => this.isGroupOptions(item))) {
        data = data.pluck('options').flatten(true);
      }

      return data.filter(op => {
                   if (this.props.multi && exclude.indexOf(op.value) > -1) return false;
                   if (this.props.filterOption) return this.props.filterOption.call(this, op, filterValue);
                   let valueTest = String(op.value), labelTest = String(op.label);
                   return !filterValue || (this.props.matchPos === 'start') ? (
                     (this.props.matchProp !== 'label' && valueTest.toLowerCase().substr(0, filterValue.length) === filterValue) ||
                     (this.props.matchProp !== 'value' && labelTest.toLowerCase().substr(0, filterValue.length) === filterValue)
                   ) : (
                     (this.props.matchProp !== 'label' && valueTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) ||
                     (this.props.matchProp !== 'value' && labelTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0)
                   );
                 }, this)
                 .value();
    }
  }
  selectFocusedOption(event) {
    return this.selectValue(this.state.focusedOption, event);
  }
  focusOption(op) {
    this.setState({
      focusedOption: op
    });
  }
  focusNextOption() {
    this.focusAdjacentOption('next');
  }
  focusPreviousOption() {
    this.focusAdjacentOption('previous');
  }
  focusAdjacentOption(dir) {
    this._focusedOptionReveal = true;

    let ops = this.state.filteredOptions;

    if (!this.state.isOpen) {
      let focusedOption = this.state.focusedOption || ops[dir === 'next' ? 0 : ops.length - 1];
      this.setState({
        isOpen: true,
        inputValue: '',
        focusedOption
      }, this._setScrollTop);
      return;
    }

    if (!ops.length) {
      return;
    }

    let focusedIndex = -1;

    for (let i = 0; i < ops.length; i++) {
      if (this.state.focusedOption === ops[i]) {
        focusedIndex = i;
        break;
      }
    }

    let focusedOption = ops[0];

    if (dir === 'next' && focusedIndex > -1 && focusedIndex < ops.length - 1) {
      focusedOption = ops[focusedIndex + 1];
    } else if (dir === 'previous') {
      if (focusedIndex > 0) {
        focusedOption = ops[focusedIndex - 1];
      } else {
        focusedOption = ops[ops.length - 1];
      }
    }

    this.setState({
      focusedOption: focusedOption
    }, this._setScrollTop);
  }
  unfocusOption(op) {
    if (this.state.focusedOption === op) {
      this.setState({
        focusedOption: null
      });
    }
  }
  buildMenu() {
    let focusedValue = this.state.focusedOption ? this.state.focusedOption.value : null;

    if(this.state.filteredOptions.length > 0) {
      focusedValue = focusedValue == null ? this.state.filteredOptions[0] : focusedValue;
    }

    let filteredOptions = this.state.filteredOptions;
    if (_.every(this.props.options, p => this.isGroupOptions(p))) {
      filteredOptions = _.chain(this.props.options)
                         .map(p => ({
                            group: p.group,
                            options: _.chain(p.options)
                                      .filter(r => _.find(filteredOptions, t => t.value === r.value))
                                      .value()
                         }))
                         .value();
    }

    return (<this.props.SelectOptionGroup { ...{
      selectedValues: () => this.state.values,
      selectContainer: () => this.refs.menu,
      SelectOption: this.props.SelectOption,
      SelectOptionGroupTitle: this.props.SelectOptionGroupTitle,
      filteredOptions: filteredOptions, //this.state.filteredOptions,
      focusedValue,
      focusOption: this.focusOption.bind(this),
      unfocusOption: this.unfocusOption.bind(this),
      selectValue: this.selectValue.bind(this),
      SelectOption: this.props.SelectOption,
      asyncOptions: this.props.asyncOptions,
      inputValue: this.state.inputValue,
      searchPromptText: this.props.searchPromptText,
      noResultsText: this.props.noResultsText
    } } />);
  }

  handleOptionLabelClick (value, event) {
    let handler = this.props.onOptionLabelClick;

    if (handler) {
      handler(value, event);
    }
  }

  setCustomItemHandler(e) {
    if (e.keyCode === 13 &&
        this.state.isOpen &&
        this.props.multi &&
        !this.state.focusedOption &&
        !this.state.isLoading &&
        !_.some(this.state.values, p => p.value === this.state.inputValue)) {

      let { values, attachedValues } = this.state;
      attachedValues = attachedValues || [];
      let newItem = {
        value: this.state.inputValue,
        label: this.state.inputValue
      };

      let newState = {
        attachedValues: attachedValues.concat(newItem),
        values: values.concat(newItem),
        isOpen: false,
        inputValue: ''
      };

      this.fireChangeEvent(newState);
      this.setState(newState);
    }
  } // setCustomItemHandler

  render() {
    let selectClass = cx('Select', this.props.className, {
      'is-multi': this.props.multi,
      'is-searchable': this.props.searchable,
      'is-open': this.state.isOpen,
      'is-focused': this.state.isFocused,
      'is-loading': this.state.isLoading,
      'is-disabled': this.props.disabled,
      'has-value': this.state.value
    });
    let value = [];

    if (this.props.multi) {
      this.state.values.forEach(function(val) {
        let props = {
          key: val.label,
          optionLabelClick: !!this.props.onOptionLabelClick,
          onOptionLabelClick: this.handleOptionLabelClick.bind(this, val),
          onRemove: this.removeValue.bind(this, val)
        };
        for (let key in val) {
          if (val.hasOwnProperty(key)) {
            props[key] = val[key];
          }
        }
        value.push(<Option {...props} />);
      }, this);
    }

    if (this.props.disabled || (!this.state.inputValue && (!this.props.multi || !value.length))) {
      value.push(<div className="Select-placeholder" key="placeholder">{this.state.placeholder}</div>);
    }

    let loading = this.state.isLoading ? (
      <span className="Select-loading" aria-hidden="true" />
    ) : null;

    let clear = this.props.multi && this.state.value && !this.props.disabled ? (
      <span className="Select-clear"
        title={this.props.multi ? this.props.clearAllText : this.props.clearValueText}
        aria-label={this.props.multi ? this.props.clearAllText : this.props.clearValueText} onMouseDown={this.clearValue}
        onClick={this.clearValue}
        dangerouslySetInnerHTML={{ __html: '&times;' }}
      />
    ) : null;

    let menu;
    let menuProps;
    if (this.state.isOpen) {
      menuProps = {
        ref: 'menu',
        className: 'Select-menu'
      };
      if (this.props.multi) {
        menuProps.onMouseDown = this.handleMouseDown;
      }
      menu = (
        <div ref="selectMenuContainer" className={ 'Select-menu-outer ' + this.props.dropdownClassName }>
          <div {...menuProps}>{this.buildMenu()}</div>
        </div>
      );
    }

    let input;
    let inputProps = {
      ref: 'input',
      className: 'Select-input',
      tabIndex: this.props.tabIndex || 0,
      onFocus: this.handleInputFocus,
      onBlur: this.handleInputBlur
    };
    for (let key in this.props.inputProps) {
      if (this.props.inputProps.hasOwnProperty(key)) {
        inputProps[key] = this.props.inputProps[key];
      }
    }

    if (this.props.addNoMatch) {
      inputProps.onKeyUp = this.setCustomItemHandler.bind(this);
    }

    if (this.props.searchable && !this.props.disabled) {
      input = <Input value={this.state.inputValue} onChange={this.handleInputChange} minWidth="5" {...inputProps} />;
    } else {
      input = <div {...inputProps}>&nbsp;</div>;
    }

    let tip = {};
    if (!this.props.multi && this.props.showSingleTooltip) {
      tip['data-tip'] = this.state.placeholder;
      tip['data-place'] = 'right';
      tip['key'] = `control${this.state.placeholder}`;
    }

    return (
      <div ref="wrapper" className={selectClass} style={this.props.style} {...tip}>
        <input type="hidden" ref="value" name={this.props.name} value={this.state.value} disabled={this.props.disabled} />
        <div className="Select-control" ref="control" onKeyDown={this.handleKeyDown} onMouseDown={this.handleMouseDown} onTouchEnd={this.handleMouseDown}>
          {value}
          {input}
          <span className="Select-arrow" />
          {loading}
          {clear}
        </div>
        {menu}
      </div>
    );
  }

}
Select.defaultProps = {
  SelectOption: SelectOption,
  SelectOptionGroupTitle: SelectOptionGroupTitle,
  SelectOptionGroup: SelectOptionGroup,
  openDisabled: false,
  addNoMatch: false,
  dropdownClassName: '',
  isOpen: false,
  showSingleTooltip: false,
  value: undefined,
  options: undefined,
  disabled: false,
  delimiter: ',',
  asyncOptions: undefined,
  autoload: true,
  placeholder: 'Select...',
  noResultsText: 'No results found',
  clearable: true,
  clearValueText: 'Clear value',
  clearAllText: 'Clear all',
  searchable: true,
  searchPromptText: 'Type to search',
  name: undefined,
  onChange: undefined,
  className: undefined,
  matchPos: 'any',
  matchProp: 'any',
  inputProps: {}
}
Select.propTypes = {
  isOpen: React.PropTypes.bool,              // whether the select is opened
  value: React.PropTypes.any,                // initial field value
  multi: React.PropTypes.bool,               // multi-value input
  disabled: React.PropTypes.bool,            // whether the Select is disabled or not
  options: React.PropTypes.array,            // array of options
  delimiter: React.PropTypes.string,         // delimiter to use to join multiple values
  asyncOptions: React.PropTypes.func,        // function to call to get options
  autoload: React.PropTypes.bool,            // whether to auto-load the default async options set
  placeholder: React.PropTypes.string,       // field placeholder, displayed when there's no value
  noResultsText: React.PropTypes.string,     // placeholder displayed when there are no matching search results
  clearable: React.PropTypes.bool,           // should it be possible to reset value
  clearValueText: React.PropTypes.string,    // title for the "clear" control
  clearAllText: React.PropTypes.string,      // title for the "clear" control when multi: true
  searchable: React.PropTypes.bool,          // whether to enable searching feature or not
  searchPromptText: React.PropTypes.string,  // label to prompt for search input
  name: React.PropTypes.string,              // field name, for hidden <input /> tag
  onChange: React.PropTypes.func,            // onChange handler: function(newValue) {}
  onFocus: React.PropTypes.func,             // onFocus handler: function(event) {}
  onBlur: React.PropTypes.func,              // onBlur handler: function(event) {}
  className: React.PropTypes.string,         // className for the outer element
  filterOption: React.PropTypes.func,        // method to filter a single option: function(option, filterString)
  filterOptions: React.PropTypes.func,       // method to filter the options array: function([options], filterString, [values])
  matchPos: React.PropTypes.string,          // (any|start) match the start or entire string when filtering
  matchProp: React.PropTypes.string,         // (any|label|value) which option property to filter on
  inputProps: React.PropTypes.object        // custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'}
}
